#include "graphics/frustrum.h"

#include "graphics/axis_aligned_bounding_box.h"
#include "graphics/oriented_bounding_box.h"
#include "graphics/bounding_sphere.h"

namespace graphics{

bool Frustrum::Overlaps(const BoundingVolume * other) const {return other->Overlaps(this);}

bool Frustrum::OverlapWith(const AxisAlignedBoundingBox * other) const {

    Vector3 to_plane;
    float distance;
    
    Vector3 box_dimension;
    float closest_point_distance;

    for(size_t i = 0; i < planes_.size(); i++) {
        to_plane = other->GetCenter() - planes_[i].first;
        distance = cml::dot(to_plane, planes_[i].second);    //find the component along the planes normal       

        //find out if the box's center lies inside or outside side of the plane
        //All plane normals point inward thus:
        //  a positive distance to the plane indicates being on the inside
        //    in this case: we still need to check to the remaining planes
        //  a negative distance to the plane indicates being on the outside
        //    in this case: we need to check if any part of the object makes it to the inside
        //      if no part does: then we are completely outside and we do not overlap with the frustrum.
        //      if some part does: then we still need the check the remaining planes
        if(distance < 0) {
            //get the distance from the center of the box to the point of the box closest to the plane
            box_dimension = other->GetHalfDimension();
            closest_point_distance = abs(cml::dot(box_dimension, planes_[i].second));

        
            //by now distance must be negative and closest_point_distance is positive            
            distance += closest_point_distance;

            //test if any part of the box makes it to the inside side of the plane
            if(distance < 0)
                return false;           
        } 
    }

    return true;
}

bool Frustrum::OverlapWith(const OrientedBoundingBox * other) const {

    Vector3 to_plane;
    float distance;
    
    Vector3 box_dimension;
    float closest_point_distance;

    for(size_t i = 0; i < planes_.size(); i++) {
        to_plane = other->GetCenter() - planes_[i].first;   //get a line to a point on the plane
        distance = cml::dot(to_plane, planes_[i].second);    //find the component along the planes normal       

        //find out if the box's center lies inside or outside side of the plane
        //All plane normals point inward thus:
        //  a positive distance to the plane indicates being on the inside
        //    in this case: we still need to check to the remaining planes
        //  a negative distance to the plane indicates being on the outside
        //    in this case: we need to check if any part of the object makes it to the inside
        //      if no part does: then we are completely outside and we do not overlap with the frustrum.
        //      if some part does: then we still need the check the remaining planes
        if(distance < 0) {
            //get the distance from the center of the box to the point of the box closest to the plane
            box_dimension = other->GetHalfDimension();
            closest_point_distance = abs(cml::dot(box_dimension, planes_[i].second));

        
            //by now distance must be negative and closest_point_distance is positive            
            distance += closest_point_distance;

            //test if any part of the box makes it to the inside side of the plane
            if(distance < 0)
                return false;           
        } 
    }

    return true;
}

bool Frustrum::OverlapWith(const BoundingSphere * other) const {

    Vector3 to_plane;
    float distance;

    for(size_t i = 0; i < planes_.size(); i++) {
        to_plane = other->GetCenter() - planes_[i].first;   //get a line to a point on the plane
        distance = cml::dot(to_plane, planes_[i].second);    //find the component along the planes normal       

        //find out if the box's center lies inside or outside side of the plane
        //All plane normals point inward thus:
        //  a positive distance to the plane indicates being on the inside
        //    in this case: we still need to check to the remaining planes
        //  a negative distance to the plane indicates being on the outside
        //    in this case: we need to check if any part of the object makes it to the inside
        //      if no part does: then we are completely outside and we do not overlap with the frustrum.
        //      if some part does: then we still need the check the remaining planes
        if(distance < 0) {
        
            //by now distance must be negative and sphere's radius is positive            
            distance += other->GetRadius();

            //test if any part of the box makes it to the inside side of the plane
            if(distance < 0)
                return false;           
        } 
    }

    return true;
}


}
